#pragma once
#include <JuceHeader.h>

namespace juce::dsp {

template <typename FloatType>
class ToneStack {
public:
    ToneStack() noexcept = default;

    //==============================================================================
    void setParams(float bass, float mid, float treble, float presence)
    {
        using FilterCoef = dsp::IIR::Coefficients<FloatType>;

        this->bass = bass;
        this->mid = mid;
        this->treble = treble;

        auto& bassFilter = chan1Chain.get<0>();
        auto bassVal = Decibels::decibelsToGain(2.f * bass - 10.f);
        bassFilter.coefficients = FilterCoef::makeLowShelf(sampleRate, 150, 0.707, bassVal);
        bassFilter.prepare(spec);

        auto& midFilter = chan1Chain.get<1>();
        auto midVal = Decibels::decibelsToGain(mid - 5.f);
        auto midQ = mid < 5 ? 1.5 : 0.707;
        midFilter.coefficients = FilterCoef::makePeakFilter(sampleRate, 450, midQ, midVal);
        midFilter.prepare(spec);

        auto& trebleFilter = chan1Chain.get<2>();
        auto trebleVal = Decibels::decibelsToGain(treble - 5.f);
        trebleFilter.coefficients = FilterCoef::makeHighShelf(sampleRate, 1800, 0.707, trebleVal);
        trebleFilter.prepare(spec);

        auto& presenceFilter = chan1Chain.get<3>();
        auto presenceVal = Decibels::decibelsToGain(presence - 5.f);
        presenceFilter.coefficients = FilterCoef::makeHighShelf(sampleRate, 4500, 0.707, presenceVal);
        presenceFilter.prepare(spec);
    }

    void setMasterVolume(FloatType masterVolume)
    {
        this->masterVolume = masterVolume / 10.f;
    }

    //==============================================================================
    /** Called before processing starts. */
    void prepare(const ProcessSpec& spec) noexcept
    {
        sampleRate = spec.sampleRate;
        this->spec = spec;
    }

    /** Processes the input and output buffers supplied in the processing context.
   */
    template <typename ProcessContext>
    void process(const ProcessContext& context) noexcept
    {
        auto&& inBlock = context.getInputBlock();
        auto&& outBlock = context.getOutputBlock();

        jassert(inBlock.getNumChannels() == outBlock.getNumChannels());
        jassert(inBlock.getNumSamples() == outBlock.getNumSamples());

        auto len = inBlock.getNumSamples();
        auto numChannels = inBlock.getNumChannels();

        if (context.isBypassed) {
            if (context.usesSeparateInputAndOutputBlocks())
                outBlock.copyFrom(inBlock);

            return;
        }
        chan1Chain.process(context);
        outBlock *= masterVolume;
    }

private:
    //==============================================================================
    using IirFilter = dsp::IIR::Filter<FloatType>;
    FloatType bass = 0, mid = 0, treble = 0, presence = 0;
    double sampleRate = 44100.f;
    FloatType masterVolume = 0.5f;
    dsp::ProcessorChain<IirFilter, IirFilter, IirFilter, IirFilter>
        chan1Chain;
    ProcessSpec spec;
};

} // namespace juce::dsp
